package poolserver

import (
	"encoding/binary"
	"net"
	"sync/atomic"

	"go.uber.org/zap"
)

//func (ctx *ConnContext) transLeftDataToBackend() error {
//    var err error
//    var isSrc bool
//	zap.S().Infof("Client(%d): SB: data=%v", ctx.Pid, ctx.rewritebuf[:])
//	err, isSrc = transData(ctx.cliConn, ctx.pBackConn.Conn, ctx.leftLen, ctx.recvBuf[:])
//	if err != nil {
//		if isSrc { /* 是客户端连接中断*/
//			zap.S().Infof("Client(%d): SB(%d): forward failed: client error", ctx.Pid, ctx.pBackConn.Id)
//			/* 做一些清理工作，把此后端连接上的事务回滚掉*/
//			/* FIXME: 有可能需要把后端连接也停掉*/
//			if ctx.transState != 'I' {
//				CleanupTrans(ctx.pBackConn.Conn)
//			}
//			atomic.StoreInt32(&ctx.pBackConn.State, 0)
//			return err
//		} else { /* 后端连接中断 */
//			zap.S().Infof("Client(%d, Q): SB(%d): forward failed: backend error", ctx.Pid, ctx.pBackConn.Id)
//			ctx.cliConn.Close()
//			ctx.pBackConn.Reconnect()
//			atomic.StoreInt32(&ctx.pBackConn.State, 0)
//			return err
//		}
//	}
//	return nil
//}

/*
返回clientErr, backendErr
*/
func (ctx *ConnContext) forwardToClientOld() (error, error) {
	var isSuccess bool
	var n int
	var clientErr error = nil
	var backendErr error = nil
	var pos int
	var pktpos int
	var pktlen int
	var msgType byte

	//var partialHappened bool = false

	//TCHDEBUG zap.S().Infof("*****RETURN Backend(%d) => Client(%d)", ctx.pBackConn.Id, ctx.Pid)

	if ctx.pSendBackConn.Id != ctx.pBackConn.Id {
		zap.S().Panicf("recv backend not the send backend!!!")
	}

	isSuccess = true
	pos = 0    //记录缓冲区记录数据的点
	pktpos = 0 //记录消息的开始位置
	for {
		//partialHappened = false
		//TCHDEBUG zap.S().Infof("Client(%d): RB(%d): begin ...", ctx.Pid, ctx.pBackConn.Id)
		n, backendErr = ctx.pBackConn.Conn.Read(ctx.recvBuf[pos : DEFAULT_BUF_SIZE-MAX_ERROR_MSG_SIZE])
		if backendErr != nil {
			zap.S().Infof("Client(%d): RB(%d): ERROR: %s", ctx.Pid, ctx.pBackConn.Id, backendErr.Error())
			ctx.cliConn.Close()
			ctx.BeReconnect()
			return clientErr, backendErr
		}
		pos += n
		for pktpos < pos {
			msgType = ctx.recvBuf[pktpos]
			/*保证包中的4字节长度字节都被接收下来了(如果没有收下来，会再次收下来*/
			if pktpos > pos-5 {
				//partialHappened = true
				//TCHDEBUG zap.S().Infof("Client(%d): RB(%d): begin ...", ctx.Pid, ctx.pBackConn.Id)
				n, backendErr = ctx.pBackConn.Conn.Read(ctx.recvBuf[pos:DEFAULT_BUF_SIZE])
				if backendErr != nil {
					zap.S().Infof("Client(%d): RB(%d): ERROR: %s", ctx.Pid, ctx.pBackConn.Id, backendErr.Error())
					ctx.cliConn.Close()
					ctx.BeReconnect()
					return clientErr, backendErr
				}
				pos += n
				continue
			}

			pktlen = int(binary.BigEndian.Uint32(ctx.recvBuf[pktpos+1 : pktpos+5]))
			if pktlen > int(DEFAULT_BUF_SIZE) || pktlen < 4 {
				zap.S().Infof("message is too long(more then %d)", DEFAULT_BUF_SIZE)
			}
			//zap.S().Infof("Client(%d): RB(%d): %c%d", ctx.Pid, ctx.pBackConn.Id, msgType, pktlen)

			/* 保证Z、E包都接收下来了*/
			if (msgType == 'Z' || msgType == 'E') && pktpos+pktlen+1 > pos {
				if pktlen > int(MAX_ERROR_MSG_SIZE)-1 {
					zap.S().Panicf("E message too long(%d more then %d)!!!", pktlen, MAX_ERROR_MSG_SIZE-1)
				}

				//TCHDEBUG zap.S().Infof("Client(%d): RB(%d): begin ...", ctx.Pid, ctx.pBackConn.Id)
				n, backendErr = ctx.pBackConn.Conn.Read(ctx.recvBuf[pos:DEFAULT_BUF_SIZE])
				if backendErr != nil {
					zap.S().Infof("Client(%d): RB(%d): ERROR: %s", ctx.Pid, ctx.pBackConn.Id, backendErr.Error())
					ctx.cliConn.Close()
					ctx.BeReconnect()
					return clientErr, backendErr
				}
				pos += n
				continue

			}

			if msgType == 'Z' {
				ctx.transState = ctx.recvBuf[pktpos+5]
			}
			if msgType == 'E' {
				isSuccess = false
				printBackendErrorMessage(ctx.recvBuf[pktpos:])
			}

			pktpos += pktlen + 1
		}

		//if pktpos > pos {
		//	zap.S().Infof("pktpos(%d) > pos(%d)", pktpos, pos)
		//}

		//if partialHappened {
		//	zap.S().Infof("partial happened!!!")
		//}

		if clientErr == nil {
			//TCHDEBUG zap.S().Infof("Client(%d): SC: data=%c%d%v", ctx.Pid, ctx.recvBuf[0], n, ctx.recvBuf[:n])
			_, clientErr = sendData(ctx.cliConn, ctx.recvBuf[0:pos])
			if clientErr != nil {
				zap.S().Infof("Client(%d): SC: ERROR: %s", ctx.Pid, clientErr.Error())
				ctx.cliConn.Close()
			}
		}

		if msgType == 'Z' && pktpos == pos {
			break
		}

		pktpos -= pos
		pos = 0
	}

	if clientErr != nil {
		isSuccess = false
	}
	if isSuccess {
		if !ctx.isInUnnamedPrepared {
			for prepareName, cuPre := range ctx.cupreMap {
				var pdata *PrepareData
				if cuPre.prepareRequestLen == 0 { /* 这是prepare的数据已缓存，而之前这个backend没有parse的情况*/
					var ok bool
					pdata, ok = ctx.cachedPrepare.Get(prepareName)
					if !ok {
						zap.S().Panicf("BUG!!!!!!")
					}
				} else {
					pdata = new(PrepareData)
					ctx.cachedPrepare.AddPrepare(prepareName, pdata)
					pdata.PrepareRequest = cuPre.prepareRequestData
				}

				pdata.BackendMap = make(map[int32]*PrepareInBackend)
				pib := new(PrepareInBackend)
				pib.PrepareId = cuPre.backendPrepareId
				pib.BackConn = ctx.pBackConn
				pib.ReConnCnt = ctx.pBackConn.ReConnCnt /* 把后端连接的当前重连次数记录下来，如果在以后，后端重连后，两者就不一样了，就知道后端被重连了*/
				pdata.BackendMap[ctx.pBackConn.Id] = pib
				zap.S().Infof("Client(%d): store prepare name=%s to backend(%d), backendPrepareId=%d",
					ctx.Pid, prepareName, ctx.pBackConn.Id, cuPre.backendPrepareId)
			}
			if len(ctx.cupreMap) > 1 {
				zap.S().Infof("many prepare in one request!")
			}
		}
	}

	/*把临时放prepare data的map清空*/
	ctx.cupreMap = make(map[string]*CuPre)

	if (clientErr != nil) || (backendErr != nil) { /*出现一些问题*/
		/* 做一些清理工作，把此后端连接上的事务回滚掉*/
		if ctx.transState != 'I' {
			zap.S().Infof("Client(%d, S): Client is closed, cleanup backend(%d) transacation",
				ctx.Pid, ctx.pBackConn.Id)
			CleanupTrans(ctx.pBackConn.Conn)
		}
		if ctx.isGetBackend {
			zap.S().Infof("Client(%d, S): release backend(%d)", ctx.Pid, ctx.pBackConn.Id)
			atomic.StoreInt32(&ctx.pBackConn.State, 0)
			ctx.isGetBackend = false
		}
		return clientErr, backendErr
	}

	if !isSuccess {
		//zap.S().Panicf("Client(%d, S): ERROR debug!!!", ctx.Pid)
	}

	/*这是没有发生错误的情况*/
	if ctx.transState == 'I' { /*释放后端连接*/
		//TCHDEBUG zap.S().Infof("Client(%d, S): release backend(%d)", ctx.Pid, ctx.pBackConn.Id)
		atomic.StoreInt32(&ctx.pBackConn.State, 0)
		ctx.isGetBackend = false
		ctx.pBackConn = nil
	}
	return nil, nil
}

/*
返回clientErr, backendErr
*/
func (ctx *ConnContext) forwardToClient2() (error, error) {
	var isSuccess bool
	var n int32
	var clientErr error = nil
	var backendErr error = nil

	//TCHDEBUG zap.S().Infof("*****RETURN Backend(%d) => Client(%d)", ctx.pBackConn.Id, ctx.Pid)

	if ctx.pSendBackConn.Id != ctx.pBackConn.Id {
		zap.S().Panicf("recv backend not the send backend!!!")
	}

	isSuccess = true
	for {
		//TCHDEBUG zap.S().Infof("Client(%d): RB(%d): begin ...", ctx.Pid, ctx.pBackConn.Id)
		n, _, backendErr = recvMessage(ctx.pBackConn.Conn, ctx.recvBuf[:])
		if backendErr != nil {
			zap.S().Infof("Client(%d): RB(%d): ERROR: %s", ctx.Pid, ctx.pBackConn.Id, backendErr.Error())
			ctx.cliConn.Close()
			ctx.BeReconnect()
			return clientErr, backendErr
		}
		//TCHDEBUG zap.S().Infof("Client(%d): RB(%d): data=%c%d%v", ctx.Pid, ctx.pBackConn.Id, ctx.recvBuf[0], n, ctx.recvBuf[:n])
		//zap.S().Infof("#### Client(%d): RB(%c)", ctx.Pid, ctx.recvBuf[0])
		if ctx.recvBuf[0] == 'Z' { /*ReadyForQuery*/
			ctx.transState = ctx.recvBuf[5]
		}

		if ctx.recvBuf[0] == 'E' {
			isSuccess = false
			printBackendErrorMessage(ctx.recvBuf[:])
		}

		if clientErr == nil {
			//TCHDEBUG zap.S().Infof("Client(%d): SC: data=%c%d%v", ctx.Pid, ctx.recvBuf[0], n, ctx.recvBuf[:n])
			n, clientErr = sendData(ctx.cliConn, ctx.recvBuf[0:n])
			if clientErr != nil {
				zap.S().Infof("Client(%d): SC: ERROR: %s", ctx.Pid, clientErr.Error())
				ctx.cliConn.Close()
			}
		}

		if ctx.recvBuf[0] == 'Z' { /* Ready for Query */
			break
		}
	}
	if clientErr != nil {
		isSuccess = false
	}
	if isSuccess {
		if !ctx.isInUnnamedPrepared {
			for prepareName, cuPre := range ctx.cupreMap {
				var pdata *PrepareData
				if cuPre.prepareRequestLen == 0 { /* 这是prepare的数据已缓存，而之前这个backend没有parse的情况*/
					var ok bool
					pdata, ok = ctx.cachedPrepare.Get(prepareName)
					if !ok {
						zap.S().Panicf("BUG!!!!!!")
					}
				} else {
					pdata = new(PrepareData)
					ctx.cachedPrepare.AddPrepare(prepareName, pdata)
					pdata.PrepareRequest = cuPre.prepareRequestData
				}

				pdata.BackendMap = make(map[int32]*PrepareInBackend)
				pib := new(PrepareInBackend)
				pib.PrepareId = cuPre.backendPrepareId
				pib.BackConn = ctx.pBackConn
				pib.ReConnCnt = ctx.pBackConn.ReConnCnt /* 把后端连接的当前重连次数记录下来，如果在以后，后端重连后，两者就不一样了，就知道后端被重连了*/
				pdata.BackendMap[ctx.pBackConn.Id] = pib
				zap.S().Infof("Client(%d): store prepare name=%s to backend(%d), pid=%d",
					ctx.Pid, prepareName, ctx.pBackConn.Id, cuPre.backendPrepareId)
			}
			if len(ctx.cupreMap) > 1 {
				zap.S().Infof("many prepare in one request!")
			}
		}
	}

	/*把临时放prepare data的map清空*/
	ctx.cupreMap = make(map[string]*CuPre)

	if (clientErr != nil) || (backendErr != nil) { /*出现一些问题*/
		/* 做一些清理工作，把此后端连接上的事务回滚掉*/
		if ctx.transState != 'I' {
			zap.S().Infof("Client(%d, S): Client is closed, cleanup backend(%d) transacation",
				ctx.Pid, ctx.pBackConn.Id)
			CleanupTrans(ctx.pBackConn.Conn)
		}
		if ctx.isGetBackend {
			zap.S().Infof("Client(%d, S): release backend(%d)", ctx.Pid, ctx.pBackConn.Id)
			atomic.StoreInt32(&ctx.pBackConn.State, 0)
			ctx.isGetBackend = false
		}
		return clientErr, backendErr
	}

	if !isSuccess {
		//zap.S().Panicf("Client(%d, S): ERROR debug!!!", ctx.Pid)
	}

	/*这是没有发生错误的情况*/
	if ctx.transState == 'I' { /*释放后端连接*/
		//TCHDEBUG zap.S().Infof("Client(%d, S): release backend(%d)", ctx.Pid, ctx.pBackConn.Id)
		atomic.StoreInt32(&ctx.pBackConn.State, 0)
		ctx.isGetBackend = false
		ctx.pBackConn = nil
	}
	return nil, nil
}

func ExtractSQL(buf []byte) string {
	var pos int32
	var begin int32
	//var prepareName string
	if buf[0] != 'P' {
		return ""
	}
	pos = 5
	for pos = 5; buf[pos] != 0; pos++ {
	}
	//prepareName = string(buf[5:pos])
	pos++
	begin = pos
	for pos = begin; buf[pos] != 0; pos++ {
	}
	return string(buf[begin:pos])
}

/*
转发数据的函数
*/
func transData(srcConn net.Conn, dstConn net.Conn, dataLen int32, buf []byte) (error, bool) {
	var err error
	var ret int
	var oprSize int32
	var pos int32
	var sendLen int32
	var leftSize = dataLen

	for leftSize > 0 {
		if leftSize > DEFAULT_BUF_SIZE {
			oprSize = DEFAULT_BUF_SIZE
		} else {
			oprSize = leftSize
		}
		ret, err = srcConn.Read(buf[0:oprSize])
		if err != nil {
			return err, true
		}

		sendLen = int32(ret)
		pos = 0
		for pos < sendLen {
			ret, err = dstConn.Write(buf[pos:sendLen])
			if err != nil {
				return err, false
			}
			pos += int32(ret)
		}
		leftSize -= sendLen
	}
	return nil, false
}
